# -*- coding:utf-8 -*-
import logging
import os
import sys

import uuid

if sys.version_info.major == 3 and sys.version_info.minor >= 6:
    from concurrent_log_handler import ConcurrentRotatingFileHandler
else:
    from logging.handlers import TimedRotatingFileHandler

LOG_INFO = {
        'LogLevel': 10,  # 等级
        'LogSave': '1',  # 是否存储日志
        'LogSavePath': './log',  # 存储日志路径
        'LogSaveCycle': 1,  # 周期
        'LogSaveUnit': 'H',  # 周期单位 D,H,M,S,天，时，分，秒
        'LogSaveNumber': 168,  # 存储日志个数
        'LogSaveSize': 5,  # 文件大小
        }


class LoadLog(object):
    """docstring for LoadLog"""
    config = LOG_INFO

    def __init__(self, config = None, name = 'main', callback = None):
        super(LoadLog, self).__init__()
        self.callback = callback

        self.config = self.get_config(config)

        self.logger = logging.getLogger(name)
        self.logger.propagate = False  # 禁用重复
        self.logger.setLevel(10)

        self.__init()

    def __init(self):
        level = self.config.get('LogLevel', 10)
        self.debug = self.logger.debug
        self.info = self.logger.info
        self.warning = self.logger.warning
        self.error = self.logger.error
        self.critical = self.logger.critical
        self.exception = self.logger.exception

        self.set_console_handler(level)
        self.set_ui_handler(level)
        self.set_file_handler(level)

    # 设置控制台日志器
    def set_console_handler(self, level):
        formatter = logging.Formatter(
                " * 控制台:%(asctime)s 文件:%(filename)s 行:%(lineno)d 级别:%(levelname)s 信息:%(message)s\n")
        formatter.datefmt = "%Y-%m-%d %H:%M:%S"
        # 控制台日志
        handler = logging.StreamHandler()
        # 日志等级
        handler.setLevel(level)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def set_ui_handler(self, level):
        if not self.callback: return

        formatter = logging.Formatter("%(asctime)s %(levelno)s %(message)s")
        formatter.datefmt = "%Y-%m-%d %H:%M:%S"

        handler = QtHandler(self.callback)
        handler.setLevel(level)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def set_file_handler(self, level):
        if not self.config.get('LogSave') in ('1', 1): return

        formatter = logging.Formatter(
                "{}\n * %(asctime)s %(filename)s %(lineno)d %(levelname)s %(message)s\n".format('*' * 80))
        formatter.datefmt = "%Y-%m-%d %H:%M:%S"

        if self.config.get('LogSavePath') == './log':
            self.config['LogSavePath'] = os.path.join('./', os.path.splitext(os.path.basename(sys.argv[0]))[0], 'log')
        logpath = os.path.abspath(self.config.get('LogSavePath'))
        try:
            os.makedirs(logpath)
            self.info(" * 日志文件夹创建成功")
        except Exception as e:
            self.warning(e)
            pass
        self.debug('日志存储在：{}'.format(logpath))

        if self.config.get('LogSaveSize') and sys.version_info.minor >= 6:
            log_file_handler = ConcurrentRotatingFileHandler(logpath + "/log", "a",
                                                             int(self.config.get('LogSaveSize')) * 1024 * 1024,
                                                             self.config.get('LogSaveNumber'))
        else:
            log_dir = logpath + "\\log"
            unit = self.config.get('LogSaveUnit', 'D')
            cycle = int("%s" % self.config.get('LogSaveCycle', 1))
            count = self.config.get('LogSaveNumber')
            log_file_handler = TimedRotatingFileHandler(filename = log_dir,
                                                        when = "%s" % unit,
                                                        interval = cycle,
                                                        backupCount = count)

            if self.config.get('LogSaveUnit') == "D":
                log_file_handler.suffix = "%Y-%m-%d.log"
            elif self.config.get('LogSaveUnit') == "H":
                log_file_handler.suffix = "%Y-%m-%d_%H.log"
            elif self.config.get('LogSaveUnit') == "M":
                log_file_handler.suffix = "%Y-%m-%d_%H-%M.log"
            elif self.config.get('LogSaveUnit') == "S":
                log_file_handler.suffix = "%Y-%m-%d_%H-%M-%S.log"
        log_file_handler.setFormatter(formatter)
        log_file_handler.setLevel(level)
        self.logger.addHandler(log_file_handler)

    @staticmethod
    def get_config(config):
        if not config:
            return LOG_INFO
        return config


# Qt
class QtHandler(logging.Handler):
    """docstring for QtHandler"""

    def __init__(self, callback):
        super(QtHandler, self).__init__()
        self.callback = callback

    def emit(self, record):
        record = self.format(record)
        self.callback.emit(record)


class Logger():
    def __init__(self, config = None, name = __name__, ui_log_callback = None):
        """[summary]

        [标准日志模块封装]

        参数:
            config: [日志配置参数dict类型] (default: {LogLevel:等级0-50/步进10，LogSave:是否保存日志，1/0,LogSavePath:日志存储路径，time:日志切割周期 D/H/M/S:N,如每小时1个=H:1,LogSaveNumber:存储日志最多数量 })
        """
        self.logger = logging.getLogger(name)
        self.callback = ui_log_callback  # 回调
        self.config = config
        # 日志总等级
        self.get_config()

    # 获取配置
    def get_config(self):
        if not self.config:
            self.config = LOG_INFO

        for key, value in self.config.items():
            if not isinstance(value, str):
                continue
            if value and value.isdigit():
                self.config[key] = int(value)
                continue
            if value and value in ('true', 'True', 'false', 'False'):
                value = value.capitalize()
            try:
                self.config[key] = eval(value)
            except:
                pass

        self.level = int(self.config.get('LogLevel') or self.config.get('logLevel'))
        self.logger.setLevel(10)
        self.logger.propagate = False  # 禁用重复
        self.init_logger()

    # 增加日志处理
    def init_logger(self):
        self.debug = self.logger.debug
        self.info = self.logger.info
        self.warning = self.logger.warning
        self.error = self.logger.error
        self.critical = self.logger.critical
        self.exception = self.logger.exception
        self.log = self.logger.log
        self.setLevel = self.logger.setLevel
        self.add_save_handler()
        self.add_console_handler()
        self.add_qtui_handler()

        # 文件日志

    # 设置日志输出控制台
    def add_console_handler(self):
        formatter = logging.Formatter(
                "{}\n * 控制台：%(asctime)s %(levelname)7s %(message)s\n{}\n".format('*' * 80, '*' * 80))
        # 控制台日志
        handler = logging.StreamHandler()
        # 日志等级
        handler.setLevel(self.level)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    # 设置日志存储到文件
    def add_save_handler(self):
        formatter = logging.Formatter("{}\n * %(asctime)s %(levelname)7s %(message)s\n{}\n".format('*' * 80, '*' * 80))
        if self.config.get('LogSave') in ('1', 1):
            if self.config.get('LogSavePath') == './log':
                self.config['LogSavePath'] = os.path.join('./', os.path.splitext(os.path.basename(sys.argv[0]))[0],
                                                          'log')
            logpath = os.path.abspath(self.config.get('LogSavePath'))
            try:
                os.mkdir(logpath)
                print(" * 日志文件夹创建成功")
            except:
                pass  # print(" * 日志文件夹已存在")
            self.debug('日志存储在：{}'.format(logpath))
            if self.config.get('LogSaveSize') and sys.version_info.minor >= 6:
                log_file_handler = ConcurrentRotatingFileHandler(logpath + "/log", "a",
                                                                 int(self.config.get('LogSaveSize')) * 1024 * 1024,
                                                                 self.config.get('LogSaveNumber'))
            else:
                log_dir = logpath + "\\log"
                log_file_handler = TimedRotatingFileHandler(filename = log_dir,
                                                            when = "%s" % self.config.get('LogSaveUnit'),
                                                            interval = int("%s" % self.config.get('LogSaveCycle')),
                                                            backupCount = self.config.get('LogSaveNumber'))

                if self.config.get('LogSaveUnit') == "D":
                    log_file_handler.suffix = "%Y-%m-%d.log"
                elif self.config.get('LogSaveUnit') == "H":
                    log_file_handler.suffix = "%Y-%m-%d_%H.log"
                elif self.config.get('LogSaveUnit') == "M":
                    log_file_handler.suffix = "%Y-%m-%d_%H-%M.log"
                elif self.config.get('LogSaveUnit') == "S":
                    log_file_handler.suffix = "%Y-%m-%d_%H-%M-%S.log"
            log_file_handler.setFormatter(formatter)
            log_file_handler.setLevel(self.config.get('LogLevel', 10))
            self.logger.addHandler(log_file_handler)

    # 设置Qt日志输出控制LogLevel
    def add_qtui_handler(self):
        if self.callback:
            handler = QtHandler(self.callback)
            handler.setLevel(self.level)
            formatter = logging.Formatter("%(asctime)10s%(levelno)s%(message)s")
            handler.setFormatter(formatter)
            self.logger.addHandler(handler)

    # 检测日志表存不存在创建
    def check_log_table(self, conn):
        db_type = conn.get("type", "").lower()
        conn = conn.get("connect")
        sql = ""
        if db_type in ['mssql', 'sql server', 'sqlserver', 'mysql']:
            sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='ICS_SERVICE_LOG'"
        elif db_type == "oracle":
            sql = "SELECT TABLE_NAME FROM USER_TABLES  WHERE TABLE_NAME='ICS_SERVICE_LOG'"
        table_data = conn.loadSql(sql)
        create_table_sql = ""
        # 表存在
        if table_data:
            return True
        # self.warning("数据库不存在日志表，创建日志表。。。")
        if not table_data and db_type == 'mysql':
            create_table_sql = """
CREATE TABLE ICS_SERVICE_LOG  (
  ID varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  SERVICE_NAME varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  SERVICE_TYPE int NULL DEFAULT 0,
  LOG_TIME datetime(6) NULL DEFAULT NULL,
  LOG_LEVEL int NULL DEFAULT NULL,
  LOG_INFO varchar(4000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  WRITE_LOG_USER varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  FLAG varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  LOG_UTC int NULL DEFAULT NULL,
  PRIMARY KEY (ID) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
"""
        elif not table_data and db_type == 'oracle':
            create_table_sql = """
CREATE TABLE ICS_SERVICE_LOG (
    ID VARCHAR2 (36) NOT NULL,
    SERVICE_NAME VARCHAR ( 50 ),
    SERVICE_TYPE NUMBER ( 10, 0 ) DEFAULT 0,
    LOG_TIME DATE DEFAULT SYSDATE,
    LOG_LEVEL NUMBER ( 10, 0 ),
    LOG_INFO VARCHAR2 ( 4000 ),
    WRITE_LOG_USER VARCHAR ( 50 ),
    FLAG VARCHAR ( 2 ),
    LOG_UTC NUMBER ( 11, 0 )
)"""

        elif not table_data and db_type in ['mssql', 'sql server', 'sqlserver']:
            create_table_sql = """
CREATE TABLE [dbo].[ICS_SERVICE_LOG] (
  [ID] varchar(36)  NOT NULL,
  [SERVICE_NAME] varchar(50) COLLATE Chinese_PRC_CI_AS  NULL,
  [SERVICE_TYPE] int  NULL DEFAULT 0,
  [LOG_TIME] datetime2(0)  NULL,
  [LOG_LEVEL] int  NULL,
  [LOG_INFO] varchar(4000) COLLATE Chinese_PRC_CI_AS  NULL,
  [WRITE_LOG_USER] varchar(50) COLLATE Chinese_PRC_CI_AS  NULL,
  [FLAG] varchar(2) COLLATE Chinese_PRC_CI_AS  NULL,
  [LOG_UTC] int  NULL,
  PRIMARY KEY CLUSTERED ([ID])
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)  

"""
        if create_table_sql:
            receive = conn.execSql(create_table_sql)
            if type(receive) == type(1):
                self.info("创建日志表：ICS_SERVICE_LOG 表成功")
                return True
            else:
                self.warning("创建日志表：ICS_SERVICE_LOG 表失败，语句：" + create_table_sql)

    # 插入日志到数据库
    def insert_log(self, conn, log_list):
        if not conn:
            return
        database_type = conn.get("type", "").lower()
        conn = conn.get("connect")
        sql = "INSERT INTO ICS_SERVICE_LOG (ID,SERVICE_NAME,LOG_TIME,LOG_LEVEL,LOG_INFO,WRITE_LOG_USER, LOG_UTC)VALUES"
        if database_type in ['mysql', 'mssql', 'sql server', 'sqlserver']:
            sql += " (%s,%s,%s,%s,%s,%s,%s)"
        elif database_type == "oracle":
            sql += " ( :1, :1, :2, :4, :5, :6, :7 )"
        guid = str(uuid.uuid1())
        log_list.insert(0, guid)
        if sql and log_list and conn:
            values = [tuple(log_list), ]
            try:
                conn.execSql(sql, values)
            except Exception as e:
                self.warning("日志写入数据库失败:{}".format(e))


# 全局捕获异常存储日志


# 捕获异常
# sys.excepthook = error
if __name__ == '__main__':
    Log = LoadLog()
    # print(dir(Log))
    Log.debug('chat xxxx')
    # print(22222222222,dsgaga)
    # Log.logger.setLevel(30)
    # Log.logger.info('xxxx')
    # Log.setSaveLog()

# 模块级函数
#
# logging.getLogger([name]):返回一个logger对象，如果没有指定名字将返回root logger
# logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()：设定root logger的日志级别
# logging.basicConfig():用默认Formatter为日志系统建立一个StreamHandler，设置基础配置并加到root logger中
#
# Loggers
#
# Logger.setLevel(lel):指定最低的日志级别，低于lel的级别将被忽略。debug是最低的内置级别，critical为最高
# Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
# Logger.addHandler(hdlr)、Logger.removeHandler(hdlr)：增加或删除指定的handler
# Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()：可以设置的日志级别

# Handlers
#
# handler对象负责发送相关的信息到指定目的地。可以通过addHandler()方法添加多个多handler
# Handler.setLevel(lel):指定被处理的信息级别，低于lel级别的信息将被忽略
# Handler.setFormatter()：给这个handler选择一个格式
# Handler.addFilter(filt)、Handler.removeFilter(filt)：新增或删除一个filter对象

# Formatters
#
# Formatter对象设置日志信息最后的规则、结构和内容，默认的时间格式为%Y-%m-%d %H:%M:%S，下面是Formatter常用的一些信息


# %(name)s                       Logger的名字
#
# %(levelno)s                    数字形式的日志级别
#
# %(levelname)s                文本形式的日志级别
#
# %(pathname)s                调用日志输出函数的模块的完整路径名，可能没有
#
# %(filename)s                  调用日志输出函数的模块的文件名
#
# %(module)s                    调用日志输出函数的模块名
#
# %(funcName)s                调用日志输出函数的函数名
#
# %(lineno)d                     调用日志输出函数的语句所在的代码行
#
# %(created)f                    当前时间，用UNIX标准的表示时间的浮 点数表示
#
# %(relativeCreated)d        输出日志信息时的，自Logger创建以 来的毫秒数
#
# %(asctime)s                  字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
#
# %(thread)d                   线程ID。可能没有
#
# %(threadName)s           线程名。可能